package com.smx.firetag;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.File;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class TemplateMolder {
	private Hashtable<String, String> variableHolder;	
	private HashMap<String, String> extractedDatasets;	
	private HashMap<String, String> datasetMappings;	
	private HashMap<String, ArrayList<HashMap<String, String>>> variableDatasets;
	private HashMap<String, HashMap<String, String>> templateCache;
	private String moldCode;
	public TemplateMolder() {
		variableHolder = new Hashtable<String, String>();
		extractedDatasets = new HashMap<String, String>();
		datasetMappings = new HashMap<String, String>();
		templateCache = new HashMap<String, HashMap<String, String>>();
		variableDatasets = new HashMap<String, ArrayList<HashMap<String, String>>>();
	}

	/**
	 * Escape given piece of xml so we can use it 
	 * @param _in
	 * @return
	 */
	public String escapeXML(String _in){
		String sXML=_in;
		sXML=sXML.replace("<","&lt;");
		sXML=sXML.replace(">","&gt;");
		return sXML;
	}

	/**
	 * UnEscape given piece of xml so we can use it 
	 * @param _in
	 * @return
	 */
	public String unEscapeXML(String _in){
		String sXML=_in;

		sXML=sXML.replace("&lt;","<");
		sXML=sXML.replace("&gt;",">");

		return sXML;
	}

	private void checkTemplateCache(String templateSource, File srcFile){
		// Simple mechanisem to buffer templates and reload them if they have
		// been changed
		if (templateCache.containsKey(templateSource)) {
			String lastMod = (templateCache.get(templateSource)).get("lastMod");
			if (lastMod.equals(srcFile.lastModified() + "")) {
				// Check if the template file have been changed since last time
				// it was accessed
				setTemplate((templateCache.get(templateSource)).get("mold"));
				return;
			} else {
				// Remove previous template from hashmap
				templateCache.remove(templateSource);
			}
		}
	}


	/**
	 * Loads the template file from passed InputStream and sets is as the current mold
	 *     
	 */
	public void loadTemplateFile(File f) {
		File srcFile = f;
		BufferedReader br = null;
		checkTemplateCache(f.getName(), srcFile);

		try {
			br = new BufferedReader(new FileReader(srcFile));
			String thisLine;
			String moldString = "";
			while ((thisLine = br.readLine()) != null) {
				moldString += thisLine + "\n";
			}

			HashMap<String, String> dataMap = new HashMap<String, String>();
			dataMap.put("mold", moldString);
			dataMap.put("lastMod", srcFile.lastModified() + "");
			templateCache.put(f.getName(), dataMap);

			setTemplate(moldString);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				br.close();
			} catch (Exception fex) {

			}
		}

	}

	/**
	 * Loads the template file and sets it as the mold source
	 * 
	 * @param templateSource
	 *            File name of the template to load
	 */
	public void loadTemplateFile(String templateSource) {
		File srcFile = new File(templateSource);
		BufferedReader br = null;

		checkTemplateCache(templateSource, srcFile);

		try {
			br = new BufferedReader(new FileReader(srcFile));
			String thisLine;
			String moldString = "";
			while ((thisLine = br.readLine()) != null) {
				moldString += thisLine + "\n";
			}

			HashMap<String, String> dataMap = new HashMap<String, String>();
			dataMap.put("mold", moldString);
			dataMap.put("lastMod", srcFile.lastModified() + "");
			templateCache.put(templateSource, dataMap);

			setTemplate(moldString);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				br.close();
			} catch (Exception fex) {

			}
		}
	}

	/**
	 * Clears all the variables
	 */
	public void purge() {
		variableHolder = new Hashtable<String, String>();
		extractedDatasets = new HashMap<String, String>();
		datasetMappings = new HashMap<String, String>();
		variableDatasets = new HashMap<String, ArrayList<HashMap<String, String>>>();
	}

	/**
	 * Test Driver
	 * 
	 * @param args
	 */
	public static void main(String[] args) {
		TemplateMolder temp = new TemplateMolder();
		temp.loadTemplateFile("c:/template.html");
		/*
		 * temp.addVariable("{01}","String A"); temp.addVariable("{ab}","String
		 * B"); temp.addVariable("{02}","String C"); temp.setTemplate("This is
		 * {ab} and this is {02} and this is {01} again");
		 */
		ArrayList<HashMap<String, String>> rowData = new ArrayList<HashMap<String, String>>();

		for (int i = 0; i < 2; i++) {
			HashMap<String, String> map = new HashMap<String, String>();
			map.put("name", "Lucifer " + i);
			map.put("last", "Devil A" + i);
			rowData.add(map);
		}
		temp.addDataSet("satan", rowData);

		// Some WorkbookData
		rowData = new ArrayList<HashMap<String, String>>();
		for (int i = 0; i < 10; i++) {
			HashMap<String, String> map = new HashMap<String, String>();
			map.put("title", "Workbook #" + i);
			map.put("url", "./workbook_" + i + ".htm");
			rowData.add(map);
		}
		temp.addDataSet("top", rowData);

		System.out.println(temp.toString());

		try {
			Thread.sleep(5000);
		} catch (InterruptedException ex) {

		}
	}

	/**
	 * Add new dataset to the molder
	 * @param datasetname
	 * @param variables
	 */
	public void addDataSet(String datasetname, ArrayList<HashMap<String, String>> variables) {
		variableDatasets.put(datasetname, variables);
	}

	/**
	 * Addataset to to the molder from a Collection , it will be converted to iternally to ArrayList
	 * @param datasetname
	 * @param variables
	 */	
	public void addDataSet(String datasetname, Collection<HashMap<String, String>> variables) {
		ArrayList<HashMap<String, String>> converted=new ArrayList<HashMap<String, String>>();		
		for(HashMap<String,String> item: variables){
			converted.add( item );
		}		
		addDataSet(datasetname, converted);		
	}

	/**
	 * 
	 * @param _key
	 *            key format must be <code>{key}</code> key can be any legal
	 *            literal
	 * @param _value
	 */
	public void addVariable(String _key, String _value) {
		try {
			if (!_key.startsWith("{") || !_key.endsWith("}")) {
				throw new ElementException("Unsuported element type, get="
						+ _key + "\n expected={key}");
			}

			String extractedKey = _key.substring(1, _key.length() - 1);
			variableHolder.put(extractedKey, _value);
		} catch (ElementException elEx) {
			elEx.printStackTrace();
		}
	}

	public void removeVariable(String _key) {
		variableHolder.remove(_key);
	}

	public String applyMold() {
		// Extract all the dataset level elements
		String tempCode = moldCode;
		String regex = "<%dataset[^>]*%>(.*?)<%/dataset%>";
		Pattern p = Pattern.compile(regex, Pattern.MULTILINE | Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
		Matcher m = p.matcher(moldCode);

		int datasetCounter = 0;
		while (m.find()) {
			String temp = m.group();
			String regexSrc = "src=\"(.*?)\"";
			String datasrc = "";
			Pattern pSrc = Pattern.compile(regexSrc, Pattern.MULTILINE | Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
			Matcher mScr = pSrc.matcher(temp);
			if (mScr.find()) {
				datasrc = mScr.group();
				datasrc = datasrc.replace("src=\"", "");
				datasrc = datasrc.replace("\"", "");
			}
			datasetCounter++;

			// Exract the Data
			int closingTagIndex = tempCode.indexOf("%>");
			int endDataTagIndex = tempCode.indexOf("<%/dataset%>");

			String codeBlock = tempCode.substring(closingTagIndex + 2, endDataTagIndex);
			String datasetName = "dataset" + datasetCounter;
			tempCode = tempCode.replace(temp, datasetName);
			extractedDatasets.put(datasrc, codeBlock);
			datasetMappings.put(datasrc, datasetName);			
		}

		Set<Map.Entry<String, String>> set = extractedDatasets.entrySet();
		for (Map.Entry<String, String> data : set) {
			String datasetName = data.getKey();

			String codeBlock = extractedDatasets.get(datasetName);
			String generated = "";
			ArrayList<HashMap<String, String>> itemList = new ArrayList<HashMap<String, String>>();
			if (variableDatasets.containsKey(datasetName)) {
				itemList = variableDatasets.get(datasetName);
			}
			for (HashMap<String, String> items : itemList) {
				Set<Map.Entry<String, String>> keyPair = items.entrySet();
				for (Map.Entry<String, String> kp : keyPair) {
					String key = datasetName + "." + kp.getKey();
					String value=kp.getValue();

					//Find all the possible matching $ in the 'value'  and replace them with a temporary key
					//othere wise we will get Regex exception if the value contains ${blafd} or something like that

					//http://cephas.net/blog/2006/02/09/javalangillegalargumentexception-illegal-group-reference-replaceall-and-dollar-signs/
					value = Matcher.quoteReplacement( value );					
					codeBlock = codeBlock.replaceAll("\\{" + key + "\\}", value);

				}
				generated += codeBlock;
				codeBlock = extractedDatasets.get(datasetName);
			}
			//Find all the possible matching $ in the 'value'  and replace them with a temporary key
			//othere wise we will get Regex exception if the value contains ${blafd} or something like that
			//http://cephas.net/blog/2006/02/09/javalangillegalargumentexception-illegal-group-reference-replaceall-and-dollar-signs/

			generated=Matcher.quoteReplacement( generated );
			tempCode = tempCode.replaceAll(datasetMappings.get(datasetName), generated);
		}

		for (Enumeration enumVar = variableHolder.keys(); enumVar.hasMoreElements();)
		{
			String key = ((String) enumVar.nextElement());
			String value = variableHolder.get(key);

			//Find all the possible matching $ in the 'value'  and replace them with a temporary key
			//othere wise we will get Regex exception if the value contains ${blafd} or something like that

			//http://cephas.net/blog/2006/02/09/javalangillegalargumentexception-illegal-group-reference-replaceall-and-dollar-signs/
			value = Matcher.quoteReplacement( value );
			tempCode = tempCode.replaceAll("\\{" + key + "\\}", value);						
		}
		return tempCode;
	}

	public void setTemplate(String _mold) {
		moldCode = _mold;
	}

	public String toString() {
		return applyMold();
	}
}

/**
 * Thrown when we get invalid element in the molder
 */
class ElementException extends Exception {
	public ElementException() {

	}

	public ElementException(String _msg) {
		super(_msg);
	}

}